import json
from time import time 
from hashlib import md5 
import urllib2 as ur
from base64 import b64decode,b64encode

class JsonTool:

    @staticmethod
    def tojson(data):
        return json.dumps(data)

    @staticmethod
    def todict(json_data):
        try:
            return json.loads(json_data)
        except ValueError:
            print (json_data)
        raise  ValueError("can not decode to json")

    @staticmethod
    def responseData(res):
        return json.dumps({
            "type":"response",
            "res":res,
            })



class Link:
    table = set()
    from_table = set()
    self_ip = None
    def __init__(self,ip):
        self.self_ip =  ip

    def link_start(self,target):
        print("start to link to ",target)
        port = self.self_ip.split(":")[-1]
        path = ur.os.path.join(target,"linkstart",port)
        if 'http' not in path:
            path = ur.os.path.join('http://',path)
        if ur.urlopen(path).code == 200 :
            print("to add",target)
            self.table.add(target)
            return "ok"
        else:
            print("not link ")
            return "link failed"
    def link_re(self,ip):
        print("link-link :",ip)

        self.table.add(ip)


    def send_cmd(self,target,msg):
        path = ur.os.path.join(target,"links")
        if target in self.table:
            if 'http' not in path:
                path = ur.os.path.join("http://",path)
            res =  ur.urlopen(ur.os.path.join(path,b64encode(self.self_ip),b64encode(msg)))
            if res.code == 200:

                obj  = JsonTool.todict(res.read())
                if obj["res"] == "ok":
                    return 1
        return 0

    def reply(self,from_node,msg):
        path = ur.os.path.join(from_node,"linkreply")

    def broadcast(self,route,msg):
        
        res = sum(list(map(self.send_cmd,route,[msg for i in route])))

        if res > 0 : ## it means some one is ok 
            return "ok"

        return "no"
            
    def recived_msg(self,from_ip_b64,msg_b64):
        from_ip = b64decode(from_ip_b64)
        msg = b64decode(msg_b64)
        print("get from ",from_ip,msg)
        if not from_ip in self.table:
            return False,False
        obj = JsonTool.todict(msg)
        new_route =  set(obj['route'])
        obj["route"].append(self.self_ip)
        route = self.table - new_route
        return route,obj

class User:
    
    contact = {}
    connections = {}
    Unknow_ID = set() 
    reverse_contact = {} 
    def getUser(self,ip):
        try:
            return User.connections[ip]
        except KeyError:
            return None

    def getUserByTag(self,tag):
        try :
            ip = self.getIp_TagByAnother(tag,by="tag")
            return self.getUser(ip)
        except KeyError:
            return None

    def getIp_TagByAnother(self,arg,by="ip"):
        """
            by : ["ip"/"tag"]
        """
        try:
            if by == "ip":
                return User.reverse_contact[arg]
            elif by == "tag":
                return User.contact[arg]
        except KeyError:
            return None

    def login(self,ip,instance):
        if ip  in self.connections.keys():
            print("re login ! ",ip)
        User.connections[ip] = instance
        User.reverse_contact[ip] =  self.unknow_name()
        
        User.contact[self.unknow_name()] = ip
        print("login ok ")

    def _del_user(self,ip,tag):
        del User.contact[tag]
        del User.reverse_contact[ip]
        del User.connections[ip]
    
    def logout(self,ip_tag):
        if ip_tag in self.contact.keys():
            ip = self.getIp_TagByAnother(ip_tag,by="tag")
            self._del_user(ip,ip_tag)
            if ip_tag in self.Unknow_ID:
                User.Unknow_ID.remove(ip_tag)
            
        if ip_tag in self.reverse_contact.keys():
            tag = self.getIp_TagByAnother(ip_tag,by="ip")
            self._del_user(ip_tag,tag)

        print("logout successful")


    def setTag(self,ip,tag):
        try :
            old_tag = self.getIp_TagByAnother(ip,by="ip")

            User.Unknow_ID.remove(old_tag)
            del User.contact[old_tag]

            User.reverse_contact[ip] = tag
            User.contact[tag] = ip
        except KeyError:
            print("such user not login !")

    def unknow_name(self):
        id = md5(str(time() * 10**6)).hexdigest()
        unknow_name = "Unknow_" + id
        User.Unknow_ID.add(unknow_name)
        return unknow_name
